use std::num::ParseIntError;

use aes_gcm::{
    aead::{Aead, KeyInit, Nonce, Key},
    Aes256Gcm,
};
use crate::error::WError;

use super::error::WResult;


pub static WTASK_KEY: &str = "winter_lonely@foxmail.com;winter_lonely@foxmail.com";


fn get_key_and_iv(key: &str) -> WResult<([u8; 32], [u8; 12])> {
    let v = key.as_bytes();
    
    let vv = v.iter()
        .enumerate()
        .filter(| (i, _) | i % 2 == 0)
        .map(| (_, v) | v.to_owned())
        .collect::<Vec<_>>();
    let mut key = [0u8; 32];
    let mut iv = [0u8; 12];
    key.copy_from_slice(&v[..32]);
    iv.copy_from_slice(&vv[..12]);
    Ok((key, iv))
}


pub fn encrypt(data: &[u8], key: &str) -> WResult<Vec<u8>> {
    // return Ok(data.to_vec());
    let (key, iv) = get_key_and_iv(key)?;
    let key_arr = Key::<Aes256Gcm>::from(key);
    let cipher = Aes256Gcm::new(&key_arr);
    let nonce = Nonce::<Aes256Gcm>::from(iv);
    let data_enc  = cipher.encrypt(&nonce, data)?;
    Ok(data_enc)
}


pub fn decrypt(data: &[u8], key: &str) -> WResult<Vec<u8>> {
    // return Ok(data.to_vec());
    let (key, iv) = get_key_and_iv(key)?;
    let key_arr = Key::<Aes256Gcm>::from(key);
    let cipher = Aes256Gcm::new(&key_arr);
    let nonce = Nonce::<Aes256Gcm>::from(iv);
    let data_dec  = cipher.decrypt(&nonce, data)?;
    Ok(data_dec)
}


pub fn encrypt_free(data: &[u8]) -> WResult<Vec<u8>> {
    encrypt(data, WTASK_KEY)
}


pub fn decrypt_free(data: &[u8]) -> WResult<Vec<u8>> {
    decrypt(data, WTASK_KEY)
}


pub fn decrypt_str_to_str(data: &str, key: &str) -> WResult<String> {
    let data_chars = data.chars().collect::<Vec<_>>();
    if data_chars.len() % 4 != 0 {
        return Err(WError::ParseError("str len error".to_owned()));
    }
    let data_num = data_chars.chunks(4).map(| v | {
        v.iter().collect::<String>().parse::<u16>()
    }).collect::<Result<Vec<u16>, ParseIntError>>();
    if data_num.is_err() {
        return Err(WError::ParseError("str parse num error".to_owned()));
    }
    let data_num = data_num.unwrap();
    let a = data_num.len() % 8 + 8;
    let data_num_now = data_num.into_iter().enumerate().map(| (i, v) |{
        (v / ((i % a) + 4) as u16) as u8
    }).collect::<Vec<u8>>();
    let data_de = decrypt(&data_num_now, key)?;
    let de = String::from_utf8_lossy(&data_de).trim().to_string();
    Ok(de)
}


pub fn encrypt_str_to_str(data: &str, key: &str) -> WResult<String> {
    let data_en = encrypt(data.as_bytes(), key)?;
    let a = data_en.len() % 8 + 8;
    let res = data_en.into_iter().enumerate().map(| (i, v) |{
        format!("{:0>4}", v as usize * (i % a + 4))
    }).collect::<String>();
    Ok(res)
}
